package exteditapp.command;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;

import exteditapp.FileDefault;
import exteditapp.ISettings;
import exteditapp.state.StateDownloading;
import exteditapp.state.StateError;
import exteditapp.state.StateIdlingLocked;

/**
 * Locks file from editing on Plone file system.
 * @author Arturas Smorgun, arturas@smorgun.lt
 */
public class CommandLock extends ICommand  {

	/**
	 * Default constructor
	 */
	public CommandLock(FileDefault file){
		super(file);
	}
	
	/**
	 * Execution of plone lock process
	 * @return true on success and false otherwise
	 */
	public Boolean execute() {
		
		/**
		 * Begin of execution.
		 */
		
		execution = true;
		
		/**
		 * Shared connection's variables.
		 */
		
		int retryNumber = 0;
		URL myCheckUrl;
		HttpURLConnection connection;
		BufferedReader br;
		
		
		/**
		 * Check edit permissions.
		 */
		
		retryNumber = 0; //used in case of network timeout
		try{
			myCheckUrl = new URL(file.getDownloadURL()+file.getModule().URL_CHECK_EDIT);
			connection = (HttpURLConnection) myCheckUrl.openConnection();
			connection.setConnectTimeout(3000);
			connection.setReadTimeout(3000);
			connection.setDefaultUseCaches(false);
			connection.setRequestProperty("Cache-Control","no-store,max-age=0,no-cache");
			connection.setRequestProperty("Expires", "0");
			connection.setRequestProperty("Pragma", "no-cache");
			br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			String rights = br.readLine();
			connection.disconnect();
			//executed if user have no edit permissions for given file
			if(rights.compareTo("1") != 0){
				StateError newState = new StateError(file.getCurrentState().getIterator(), this.file);
				newState.setLabel("ERROR: FILE ACCESS DENIED");
				this.file.setCurrentState(newState);
				execution = false;
				file.getModule().getInvoker().setExecution(execution);
				return false;
			}
		}
		
		/**
		 * Catches passed invalid URL string.
		 */
		
		catch(MalformedURLException e){
			StateError newState = new StateError(this.file.getCurrentState().getIterator(), this.file);
			newState.setup("FATAL SYSTEM ERROR");
			this.file.setCurrentState(newState);
			return false;
		}
		
		/**
		 * Catches lost request, request rejection or server response error.
		 */
		
		catch(ConnectException e){
			StateError newState = new StateError(this.file.getCurrentState().getIterator(), this.file);
			newState.setup("SERVER NOT FOUND");
			return false;
		}
		
		/**
		 * Catches network timeout - tries to retry request 5 times before showing error message.
		 */
		
		catch(SocketTimeoutException e){
			retryNumber++;
			if(retryNumber > 5){
				StateError newState = new StateError(this.file.getCurrentState().getIterator(), this.file);
				newState.setup("CONNECTION TIMEOUT");
				this.file.setCurrentState(newState);
				return false;
			}
		}
		
		/**
		 * Catches IO exceptions (usually - everything what related with connection errors).
		 */
		
		catch(IOException e){
			StateError newState = new StateError(this.file.getCurrentState().getIterator(), this.file);
			newState.setup("CONNECTION ERROR");
			this.file.setCurrentState(newState);
			return false;
		}
			
		/**
		 * Do lock.
		 */
		
		retryNumber = 0; //used in case of network timeout
		try{
			myCheckUrl = new URL(file.getDownloadURL()+file.getModule().URL_DO_LOCK);
			connection = (HttpURLConnection) myCheckUrl.openConnection();
			br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			String locked = br.readLine();
			connection.disconnect();
			String[] lockedArray = locked.split("####");
			ISettings sets = file.getModule().getSettings();
			//executed if file locking is failed
			if(lockedArray.length == 3 && lockedArray[0].compareTo("1") == 0){
				sets.setRuntimeParameter("lockPath", lockedArray[1]);
				sets.setRuntimeParameter("lockToken", lockedArray[2]);
				sets.setRuntimeParameter("fileLocked", "1");
				
				StateDownloading newState = new StateDownloading(file.getCurrentState().getIterator(), this.file);
				this.file.setCurrentState(newState);
			}
			else if(lockedArray.length == 2 && lockedArray[0].compareTo("0") == 0){
				sets.setRuntimeParameter("lockAuthor", lockedArray[1].toUpperCase());
				sets.setRuntimeParameter("fileLocked", "1");
				
				StateIdlingLocked newState = new StateIdlingLocked(file.getCurrentState().getIterator(), this.file);
				this.file.setCurrentState(newState);
			}
			else{
				StateError newState = new StateError(file.getCurrentState().getIterator(), this.file);
				this.file.setCurrentState(newState);
			}
		}
		
		/**
		 * Catches passed invalid URL string.
		 */
		
		catch(MalformedURLException e){
			StateError newState = new StateError(this.file.getCurrentState().getIterator(), this.file);
			newState.setup("FATAL SYSTEM ERROR");
			this.file.setCurrentState(newState);
			return false;
		}
		
		/**
		 * Catches lost request, request rejection or server response error.
		 */
		
		catch(ConnectException e){
			StateError newState = new StateError(this.file.getCurrentState().getIterator(), this.file);
			newState.setup("SERVER NOT FOUND");
			return false;
		}
		
		/**
		 * Catches network timeout - tries to retry request 5 times before showing error message.
		 */
		
		catch(SocketTimeoutException e){
			retryNumber++;
			if(retryNumber > 5){
				StateError newState = new StateError(this.file.getCurrentState().getIterator(), this.file);
				newState.setup("CONNECTION TIMEOUT");
				this.file.setCurrentState(newState);
				return false;
			}
		}
		
		/**
		 * Catches IO exceptions (usually - everything what related with connection errors).
		 */
		
		catch(IOException e){
			StateError newState = new StateError(this.file.getCurrentState().getIterator(), this.file);
			newState.setup("CONNECTION ERROR");
			this.file.setCurrentState(newState);
			return false;
		}
		
		/**
		 * End of execution.
		 * Mark in Execution invoker, that this thread is almost ended it's workand next execu-
		 * tion (from queue) can be executed.
		 */
		
		execution = false;
		file.getModule().getInvoker().setExecution(execution);
		return true;
		
	}

	/**
	 * Method for command execution invocation in separated thread
	 */
	public void run() {
		execute();
	}
	
}
